/** @file

  Copyright (C) 2022 - 2023, Phytium Technology Co., Ltd. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "mcu_info.h"

//#define MCU_USE_MANUAL


/**
  Print Ddr information

  @param[in]  Channel  DDR channel
  @param[in]  DdrConfigData  DDR configuration
  @retval  None
**/
typedef struct warn_alarm_fun{
    VOID (*Alarm)(UINTN ErrorType);
}warn_alarm_fun_t;


VOID 
SendCpldCtr(
  UINT32 cmd
  )
{
  UINT32 i;
  DEBUG((EFI_D_INFO, "send cmd to cpld : %d \n", cmd));
  //start
  MmioWrite32(PWR_CTR0_REG, 0x1);
  MicroSecondDelay(1000);
  for(i = 0; i < cmd; i++){
  MmioWrite32(PWR_CTR1_REG, 0x1);
  MicroSecondDelay(2000);
  MmioWrite32(PWR_CTR1_REG, 0x0);
  MicroSecondDelay(1000);
  }
  //end
  MicroSecondDelay(2000);
  MmioWrite32(PWR_CTR0_REG, 0x0);
}

/*
VOID
Pullupshort(
  VOID
  )
{
#if 0
  UINT32  PullUp;

//Output high level 400ms
  PullUp = MmioRead32(GPIO_0_BASE + GPIO_SWPORT_DR);
  PullUp |= PULL_UP;
  MmioWrite32(GPIO_0_BASE + GPIO_SWPORT_DR, PullUp);
#endif

#if 1
  MmioWrite32(PWR_CTR1_REG, 0x1);
#endif

  MicroSecondDelay(400000);
}

VOID
Pulluplong(
  VOID
  )
{
#if 0
  UINT32  PullUp;

//Output high level 800ms
  PullUp = MmioRead32(GPIO_0_BASE + GPIO_SWPORT_DR);
  PullUp |= PULL_UP;
  MmioWrite32(GPIO_0_BASE + GPIO_SWPORT_DR, PullUp);
#endif

#if 1
  MmioWrite32(PWR_CTR1_REG, 0x1);
#endif

  MicroSecondDelay(800000);
}
*/

VOID
Pulldown(
  VOID
  )
{
//Output low level 100ms
#if 0
  UINT32  PullDown;

  PullDown = MmioRead32(GPIO_0_BASE + GPIO_SWPORT_DR);
  CLEARBIT(PullDown,2);
  MmioWrite32(GPIO_0_BASE + GPIO_SWPORT_DR, PullDown);
#endif

#if 1
  MmioWrite32(PWR_CTR1_REG, 0x0);
#endif

  MicroSecondDelay(100000);
}

VOID
AlarmCpld(
  UINTN  ErrorType
  )
{
  MmioWrite32(PWR_CTR0_REG, 0x0);

  switch(ErrorType)
  {
    case ERROR_TYPE0:  //3 short 1 long
		   SendCpldCtr(16);
		   break;
  
	case ERROR_TYPE1:  //1 short 1 long
		   SendCpldCtr(17);
		   break;
	default:
			Pulldown();
    }
}

static warn_alarm_fun_t warn_alarm_fun_cpld = {
  .Alarm = AlarmCpld
};

VOID
AlarmSe(
  UINTN  ErrorType
  )
{

  switch(ErrorType)
  {
    case ERROR_TYPE0:  //3 short 1 long
      SendSeCtr(0x20);
      break;
  
    case ERROR_TYPE1:  //1 short 1 long
      SendSeCtr(0x21);
      break;
    default:
      Pulldown();
    }
}

static warn_alarm_fun_t warn_alarm_fun_se = {
    .Alarm = AlarmSe
};

void * get_flag(void)
{
    switch(pm_get_s3_flag_source())
    {
      case 0:
        return &warn_alarm_fun_cpld;
      case 2:
        return &warn_alarm_fun_se;
      default:
        return &warn_alarm_fun_cpld;
      }

}

VOID
Alarm(
  UINTN ErrorType
  )
{
  //pwr_ctr_fun_t *pwr_flag_p = get_flag_fun();
  warn_alarm_fun_t *warn_alarm_fun_p = get_flag();

  if(NULL == warn_alarm_fun_p || NULL == warn_alarm_fun_p->Alarm){
    DEBUG((EFI_D_ERROR, "%a(),Line=%d\n", __FUNCTION__, __LINE__));
    while(1);
  }
  warn_alarm_fun_p->Alarm(ErrorType);
}
#if 1
VOID
FillNoteBookMemoryInformation(
  IN OUT  MEMORY_DEVICES_INFO   *MemoryDevInfo,
  IN  UINT8 SpdIndex
  )
{
  DEBUG((EFI_D_INFO, "FillNoteBookMemoryInformation  zhy123456\n"));
  UINT8 i ,j;
  j = 0;
  MemoryDevInfo->SpdInfo[SpdIndex].Size                  = 8;
  MemoryDevInfo->SpdInfo[SpdIndex].FormFactor            = 0x1;
  MemoryDevInfo->SpdInfo[SpdIndex].MemoryTechnology      = 0x3;
  MemoryDevInfo->SpdInfo[SpdIndex].RankNumber            = 0x1;
  MemoryDevInfo->SpdInfo[SpdIndex].MemoryType            = 0x1;
  MemoryDevInfo->SpdInfo[SpdIndex].Speed                 = 3200;
  for(i = 0; i < 20 ;i ++){
    MemoryDevInfo->SpdInfo[SpdIndex].PartNumber[j] = 0x30;
    //DEBUG((EFI_D_INFO, "the PartNumber[%d] is 0x%x\n",j,MemoryDevInfo->SpdInfo[SpdIndex].PartNumber[j]));
    j++;
  }
  MemoryDevInfo->SpdInfo[SpdIndex].PartNumber[20]  = '\0';  
}
#endif
VOID
MemoryInfoDDR3 (
  IN UINT8                Channel,
  IN UINT8               *Buffer, 
  IN MEMORY_DEVICES_INFO *MemoryDevInfo,
  IN UINT8                SpdIndex
  )
{
  DEBUG((EFI_D_INFO, "Creat DDR3 SpdInfo Hob.\n"));
}

VOID
MemoryInfoDDR4 (
  IN UINT8                    Channel,
  IN UINT8                   *Buffer,
  IN OUT MEMORY_DEVICES_INFO *MemoryDevInfo,
  IN UINT8                    SpdIndex
  )
{
  //UINT16 Temp;
  DEBUG((EFI_D_INFO, "Creat DDR4 SpdInfo Hob.\n"));
#if 1
  //MemoryDevInfo->SpdInfo[Channel].AssertTag                               =
  //MemoryDevInfo->SpdInfo[Channel].ChannelNumer                            = Channel;
  //MemoryDevInfo->SpdInfo[Channel].ConfiguredMemorySpeed                   =
  //MemoryDevInfo->SpdInfo[Channel].ConfiguredVoltage                       =
  //MemoryDevInfo->SpdInfo[Channel].DeviceSet                               = 0 ;
  //MemoryDevInfo->SpdInfo[Channel].DimmNumer                               =  nedd add param
  //MemoryDevInfo->SpdInfo[Channel].ExtendedSize                            =
  //MemoryDevInfo->SpdInfo[Channel].FirmwareVersion                         =
  //MemoryDevInfo->SpdInfo[Channel].FormFactor                              =
  //MemoryDevInfo->SpdInfo[Channel].Manufacturer                            =
  //MemoryDevInfo->SpdInfo[Channel].MaxVoltage                              = 1200;
  //MemoryDevInfo->SpdInfo[Channel].MemorSubsystemControllerManufacturerID  =
  //MemoryDevInfo->SpdInfo[Channel].MemorSubsystemControllerProductID       =
  //MemoryDevInfo->SpdInfo[Channel].MemoryType                              = 
  //MemoryDevInfo->SpdInfo[Channel].MinVoltage                              = 1200;
  //MemoryDevInfo->SpdInfo[Channel].ModuleManufacturerID                    =
  //MemoryDevInfo->SpdInfo[Channel].ModuleProductID                         =
  //MemoryDevInfo->SpdInfo[Channel].PartNumber                              =
  //MemoryDevInfo->SpdInfo[Channel].RankNumber                              =
  //MemoryDevInfo->SpdInfo[Channel].SerialNumber                            =
  //MemoryDevInfo->SpdInfo[Channel].Size                                    =
  //MemoryDevInfo->SpdInfo[Channel].Speed                                   =
#endif
}

VOID
PrintDdrInfo (
  IN UINT8                      Channel,
  IN DDR_CONFIG                *DdrConfigData,
  IN OUT MEMORY_DEVICES_INFO   *MemoryDevInfo,
  IN UINT8                      SpdIndex
  )
{
  UINT8 Temp;
  UINT16 sdram_density;
  UINT64 dimm_capacity;

  DEBUG((EFI_D_INFO, "  tAAmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tAAmin));
  DEBUG((EFI_D_INFO, "  tRCDmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tRCDmin));
  DEBUG((EFI_D_INFO, "  tRPmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tRPmin));
  DEBUG((EFI_D_INFO, "  tRASmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tRASmin));
  DEBUG((EFI_D_INFO, "  tRCmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tRCmin));
  DEBUG((EFI_D_INFO, "  tFAWmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tFAWmin));
  if(DdrConfigData->DdrSpdInfo[Channel].dram_type == DDR3_TYPE){
      DEBUG((EFI_D_INFO, "  tRRDmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tRRDmin));
      DEBUG((EFI_D_INFO, "  tWRmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tWRmin));
      DEBUG((EFI_D_INFO, "  tWTRmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tWTRmin));
  }else{
      DEBUG((EFI_D_INFO, "  tRRD_Smin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tRRD_Smin));
      DEBUG((EFI_D_INFO, "  tRRD_Lmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tRRD_Lmin));
      DEBUG((EFI_D_INFO, "  tCCD_Lmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tCCD_Lmin));
      DEBUG((EFI_D_INFO, "  tWRmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tWRmin));
      DEBUG((EFI_D_INFO, "  tWTR_Smin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tWTR_Smin));
      DEBUG((EFI_D_INFO, "  tWTR_Lmin = %dps\n", DdrConfigData->DdrSpdInfo[Channel].tWTR_Lmin));
  }

  Temp = 4 << DdrConfigData->DdrSpdInfo[Channel].data_width;
  if(DdrConfigData->DdrSpdInfo[Channel].dram_type == DDR3_TYPE){
  	sdram_density =
          (1UL << (DdrConfigData->DdrSpdInfo[Channel].row_num + DdrConfigData->DdrSpdInfo[Channel].col_num)) * Temp
          * DdrConfigData->DdrSpdInfo[Channel].bank_num >> 20; //units: Mb
  }else{
  	sdram_density =
          (1UL << (DdrConfigData->DdrSpdInfo[Channel].row_num + DdrConfigData->DdrSpdInfo[Channel].col_num)) * Temp
          * DdrConfigData->DdrSpdInfo[Channel].bg_num * DdrConfigData->DdrSpdInfo[Channel].bank_num >> 20; //units: Mb
  }
  dimm_capacity = sdram_density * DdrConfigData->DdrSpdInfo[Channel].rank_num * 64 / Temp >> 13;   //units: GB


  MemoryDevInfo->SpdInfo[SpdIndex].Size   = dimm_capacity;
  DEBUG((EFI_D_INFO, "\tDimm Capacity = %dGB\n", dimm_capacity));
  //DEBUG((EFI_D_INFO, "MemoryDevInfo->SpdInfo[Channel].Size is %lld\n", MemoryDevInfo->SpdInfo[Channel].Size));


  switch(DdrConfigData->DdrSpdInfo[Channel].dram_type){
    case 0xb:
      DEBUG((EFI_D_INFO, "\tDDR3"));
      MemoryDevInfo->SpdInfo[SpdIndex].MemoryType = MemoryTypeDdr3;
      break;
    case 0xc:
      DEBUG((EFI_D_INFO, "\tDDR4"));
      MemoryDevInfo->SpdInfo[SpdIndex].MemoryType = MemoryTypeDdr4;
      break;
    case 0xF:
      DEBUG((EFI_D_INFO, "\tLPDDR3"));
      MemoryDevInfo->SpdInfo[SpdIndex].MemoryType = MemoryTypeLpddr3;
      break;
    case 0x10:
      DEBUG((EFI_D_INFO, "\tLPDDR4"));
      MemoryDevInfo->SpdInfo[SpdIndex].MemoryType = MemoryTypeLpddr4;
      break;
    default:
      DEBUG((EFI_D_INFO, "\tDram Type=0x%x", DdrConfigData->DdrSpdInfo[Channel].dram_type));
    }

  switch(DdrConfigData->DdrSpdInfo[Channel].dimm_type) {
    case 1:
      DEBUG((EFI_D_INFO, "\tRDIMM"));
      MemoryDevInfo->SpdInfo[SpdIndex].FormFactor = MemoryFormFactorDimm;
      break;
    case 2:
      DEBUG((EFI_D_INFO, "\tUDIMM"));
      MemoryDevInfo->SpdInfo[SpdIndex].FormFactor = MemoryFormFactorDimm;
      break;
    case 3:
    case 9:
      DEBUG((EFI_D_INFO, "\tSODIMM"));
      MemoryDevInfo->SpdInfo[SpdIndex].FormFactor = MemoryFormFactorSodimm;
      break;
    case 4:
      DEBUG((EFI_D_INFO, "\tLRDIMM"));
      MemoryDevInfo->SpdInfo[SpdIndex].FormFactor = MemoryFormFactorDimm;
      break;
    default:
      DEBUG((EFI_D_INFO, "\tDimm Type=0x%x", DdrConfigData->DdrSpdInfo[Channel].dimm_type));
  }

  if(DdrConfigData->DdrSpdInfo[Channel].dram_type == DDR4_TYPE) {
  	DEBUG((EFI_D_INFO, "/%d Bank Groups", DdrConfigData->DdrSpdInfo[Channel].bg_num));
  	DEBUG((EFI_D_INFO, "/%d Banks", DdrConfigData->DdrSpdInfo[Channel].bank_num));
  } else {
  	DEBUG((EFI_D_INFO, "/%d Banks", DdrConfigData->DdrSpdInfo[Channel].bank_num));
  	DEBUG((EFI_D_INFO, "/sdram_density=%dGb", sdram_density >> 10));
  }
  DEBUG((EFI_D_INFO, "/Column %d", DdrConfigData->DdrSpdInfo[Channel].col_num));
  DEBUG((EFI_D_INFO, "/Row %d", DdrConfigData->DdrSpdInfo[Channel].row_num));
  switch(DdrConfigData->DdrSpdInfo[Channel].data_width) {
  	case DIMM_x4:
  		DEBUG((EFI_D_INFO, "/X4"));
          break;
  	case DIMM_x8:
  		DEBUG((EFI_D_INFO, "/X8"));
          break;
  	case DIMM_x16:
  		DEBUG((EFI_D_INFO, "/X16"));
          break;
  	case DIMM_x32:
  		DEBUG((EFI_D_INFO, "/X32"));
          break;
  	default:
  		DEBUG((EFI_D_INFO, "/Data Width=0x%x", DdrConfigData->DdrSpdInfo[Channel].data_width));
  }

  DEBUG((EFI_D_INFO, "/%d Rank", DdrConfigData->DdrSpdInfo[Channel].rank_num));
  switch(DdrConfigData->DdrSpdInfo[Channel].ecc_type) {
    case 0:
      DEBUG((EFI_D_INFO, "/NO ECC"));
      break;
    case 1:
      DEBUG((EFI_D_INFO, "/ECC"));
    default:
      DEBUG((EFI_D_INFO, "/Ecc Type=0x%0x", DdrConfigData->DdrSpdInfo[Channel].rank_num));
  }

  if(DdrConfigData->DdrSpdInfo[Channel].mirror_type == 0) {
    DEBUG((EFI_D_INFO, "/Standard\n"));
  } else {
    DEBUG((EFI_D_INFO, "/Mirror\n"));
  }

  DEBUG((EFI_D_INFO, "\tModual:"));
  switch(DdrConfigData->DdrSpdInfo[Channel].module_manufacturer_id) {
    case SAMSUNG_VENDOR:
      DEBUG((EFI_D_INFO, "Samsung"));
      //AsciiStrCpy(MemoryDevInfo->SpdInfo[Channel].Manufacturer,"Samsung");
      break;
    case MICRON_VENDOR:
      DEBUG((EFI_D_INFO, "Micron"));
      break;
    case HYNIX_VENDOR:
      DEBUG((EFI_D_INFO, "Hynix"));
      break;
    case KINGSTON_VENDOR:
      DEBUG((EFI_D_INFO, "KingSton"));
      break;
    case RAMAXEL_VENDOR:
      DEBUG((EFI_D_INFO, "Ramaxel"));
      break;
    case LANQI_VENDOR:
      DEBUG((EFI_D_INFO, "Lanqi"));
      break;
    default:
      DEBUG((EFI_D_INFO, "Unknown=0x%x", DdrConfigData->DdrSpdInfo[Channel].module_manufacturer_id));
  }
}

/**
  Parse DDR3 Spd information

  @param[in]  Channel  DDR Channel
  @param[in]  DDR spd information

  @retval  None
**/
VOID
ParseDDR3Spd (
  IN  UINT8                     Channel,
  IN  UINT8                    *Buffer,
  IN  DDR_CONFIG               *DdrConfigData,
  IN OUT MEMORY_DEVICES_INFO   *MemoryDevInfo,
  IN UINT8                      SpdIndex
  )
{
  UINT16 Temp;
  UINT32 Temp32;

  DEBUG((EFI_D_INFO, "Parse DDR3 SPD\n"));

  Temp = Buffer[3] & 0xf;
  DdrConfigData->DdrSpdInfo[Channel].dimm_type = Temp;

  DdrConfigData->DdrSpdInfo[Channel].bank_num = 8;

  Temp = Buffer[5] & 0x7;
  DdrConfigData->DdrSpdInfo[Channel].col_num = Temp + 9;

  Temp = Buffer[5] >> 3 & 0x7;
  DdrConfigData->DdrSpdInfo[Channel].row_num = Temp + 12;

  //Module Organization
  Temp = Buffer[7] & 0x7;
  DdrConfigData->DdrSpdInfo[Channel].data_width = Temp;

  Temp = Buffer[7] >> 3 & 0x7;
  DdrConfigData->DdrSpdInfo[Channel].rank_num= Temp + 1;

  //Module Organization
  Temp = Buffer[8] >> 3 & 0x7;
  DdrConfigData->DdrSpdInfo[Channel].ecc_type = Temp;

  if(DdrConfigData->DdrSpdInfo[Channel].dimm_type == UDIMM_TYPE){
  	Temp = Buffer[63] & 0x1;
  	DdrConfigData->DdrSpdInfo[Channel].mirror_type = Temp;
  }

  DdrConfigData->DdrSpdInfo[Channel].F0RC03 = (Buffer[70] >> 4) & MASK_4BIT;
  DdrConfigData->DdrSpdInfo[Channel].F0RC04 = Buffer[71] & MASK_4BIT;
  DdrConfigData->DdrSpdInfo[Channel].F0RC05 = (Buffer[71] >> 4) & MASK_4BIT;

  DdrConfigData->DdrSpdInfo[Channel].module_manufacturer_id = ((UINT16)Buffer[117] << 8) + Buffer[118];

  //tRCDmin
  DdrConfigData->DdrSpdInfo[Channel].tRCDmin = Buffer[18] * SPD_MTB;
  //tRPmin
  DdrConfigData->DdrSpdInfo[Channel].tRPmin = Buffer[20] * SPD_MTB;
  //tRASmin
  DdrConfigData->DdrSpdInfo[Channel].tRASmin = (((((UINT16)Buffer[21]) & 0xf) << 8) + Buffer[23]) * SPD_MTB;
  //tRCmin
  DdrConfigData->DdrSpdInfo[Channel].tRCmin = (((((UINT16)Buffer[21]>>4) & 0xf) << 8) + Buffer[23]) * SPD_MTB;
  //tFAWmin
  DdrConfigData->DdrSpdInfo[Channel].tFAWmin = ((((UINT16)Buffer[28] & 0xf) << 8) + Buffer[29]) * SPD_MTB;
  //tRRDmin
  DdrConfigData->DdrSpdInfo[Channel].tRRDmin = Buffer[19] * SPD_MTB;
  //tWRmin
  DdrConfigData->DdrSpdInfo[Channel].tWRmin = 15000;
  //tWTRmin, 7.5ns
  DdrConfigData->DdrSpdInfo[Channel].tWTRmin = 7500;

  PrintDdrInfo(Channel, DdrConfigData,MemoryDevInfo,SpdIndex);
  Temp = ((UINT16)Buffer[147] << 8) + Buffer[149];
  DEBUG((EFI_D_INFO, "/Dram:"));
  switch(Temp){
  	case SAMSUNG_VENDOR:
  	  DEBUG((EFI_D_INFO, "Samsung"));
        break;
  	case MICRON_VENDOR:
  	  DEBUG((EFI_D_INFO, "Micron"));
        break;
  	case HYNIX_VENDOR:
  	  DEBUG((EFI_D_INFO, "Hynix"));
        break;
  	case KINGSTON_VENDOR:
  	  DEBUG((EFI_D_INFO, "KingSton"));
        break;
  	case RAMAXEL_VENDOR:
  	  DEBUG((EFI_D_INFO, "Ramaxel"));
        break;
  	case LANQI_VENDOR:
  	  DEBUG((EFI_D_INFO, "Lanqi"));
        break;
  	case CXMT_VENDOR:
  	  DEBUG((EFI_D_INFO, "CXMT"));
        break;
  	case UNILC_VENDOR:
  	  DEBUG((EFI_D_INFO, "Unilc"));
        break;
  	default:
  	  DEBUG((EFI_D_INFO, "Unknown=0x%x", Temp));
  }

  Temp32 = ((UINT32)Buffer[122] << 24) + ((UINT32)Buffer[123] << 16)
  			+ ((UINT32)Buffer[124] << 8) + Buffer[125];
  DEBUG((EFI_D_INFO, "/Serial:0x%x\n", Temp32));
}

/**
  Parse DDR4 Spd information

  @param[in]  Channel  DDR Channel
  @param[in]  DDR spd information

  @retval  None
**/
VOID
ParseDDR4Spd (
  IN UINT8                      Channel,
  IN UINT8                     *Buffer,
  IN OUT DDR_CONFIG            *DdrConfigData,
  IN OUT MEMORY_DEVICES_INFO   *MemoryDevInfo,
  IN UINT8                      SpdIndex
  )
{
  UINT16  Temp;
  UINT32  Temp32;
  UINT16  i;
  UINT8   j;
  i = 0;
  j = 0;
  DEBUG((EFI_D_INFO, "Parse DDR4 Spd\n"));

  for(i = 329; i < 349 ;i ++){
    MemoryDevInfo->SpdInfo[SpdIndex].PartNumber[j] = Buffer[i];
    //DEBUG((EFI_D_INFO, "the PartNumber[%d] is 0x%x\n",j,MemoryDevInfo->SpdInfo[SpdIndex].PartNumber[j]));
    j++;
  }
  MemoryDevInfo->SpdInfo[SpdIndex].PartNumber[20] = '\0';

  Temp = Buffer[3] & 0xf;
  DdrConfigData->DdrSpdInfo[Channel].dimm_type = Temp;
  Temp = Buffer[3] >> 4;
  if(Temp == 0){
    MemoryDevInfo->SpdInfo[SpdIndex].MemoryTechnology = MemoryTechnologyDram;
  }
///Speed
  Temp = Buffer[18] *125 +(UINT8)Buffer[125] *1;
  Temp = (1000000/Temp)*2;
  MemoryDevInfo->SpdInfo[SpdIndex].Speed  = Temp;
  //DEBUG((EFI_D_INFO,"THE SPEED IS %d",MemoryDevInfo->SpdInfo[SpdIndex].Speed));
//vol
  Temp = Buffer[11];
  DEBUG((EFI_D_INFO,"the vol is  %x",Temp));

  //SDRAM Density and Banks
	Temp = Buffer[4] >> 6 & 0x3;
    if(!Temp) {
	  DdrConfigData->DdrSpdInfo[Channel].bg_num = 0x0;
    } else {
	  DdrConfigData->DdrSpdInfo[Channel].bg_num = 0x1<<Temp;
    }

	Temp = Buffer[4] >> 4 & 0x3;
    if(Temp == 1) {
	  DdrConfigData->DdrSpdInfo[Channel].bank_num = 8;
    } else {
	  DdrConfigData->DdrSpdInfo[Channel].bank_num = 4;
    }

	//SDRAM Addressing
	Temp = Buffer[5] & 0x7;
	DdrConfigData->DdrSpdInfo[Channel].col_num = Temp + 9;

	Temp = Buffer[5]>>3 & 0x7;
	DdrConfigData->DdrSpdInfo[Channel].row_num = Temp + 12;

	//Module Organization
	Temp = Buffer[12] & 0x7;
	DdrConfigData->DdrSpdInfo[Channel].data_width = Temp;

	Temp = Buffer[12]>>3 & 0x7;
	DdrConfigData->DdrSpdInfo[Channel].rank_num  = Temp + 1;
	MemoryDevInfo->SpdInfo[SpdIndex].RankNumber   = Temp + 1;

	//Module Organization
	Temp = Buffer[13]>>3 & 0x7;
	DdrConfigData->DdrSpdInfo[Channel].ecc_type = Temp;

	//(Registered): Address Mapping from Register to DRAM
	if((DdrConfigData->DdrSpdInfo[Channel].dimm_type == RDIMM_TYPE)
            || (DdrConfigData->DdrSpdInfo[Channel].dimm_type == LRDIMM_TYPE)) {

	  Temp = Buffer[136] & 0x1;
    } else {
	  Temp = Buffer[131] & 0x1;
    }

	DdrConfigData->DdrSpdInfo[Channel].mirror_type = Temp;
	DdrConfigData->DdrSpdInfo[Channel].F0RC03 = (Buffer[137]>>4) & MASK_4BIT;
	DdrConfigData->DdrSpdInfo[Channel].F0RC04 = Buffer[137] & MASK_4BIT;
	DdrConfigData->DdrSpdInfo[Channel].F0RC05 = Buffer[138] & MASK_4BIT;

	DdrConfigData->DdrSpdInfo[Channel].vrefDQ_PR0 = Buffer[140];
	DdrConfigData->DdrSpdInfo[Channel].vrefDQ_MDRAM = Buffer[144];
	DdrConfigData->DdrSpdInfo[Channel].RTT_MDRAM_1866 = Buffer[145];
	DdrConfigData->DdrSpdInfo[Channel].RTT_MDRAM_2400 = Buffer[146];
	DdrConfigData->DdrSpdInfo[Channel].RTT_MDRAM_3200 = Buffer[147];
	DdrConfigData->DdrSpdInfo[Channel].Drive_DRAM = Buffer[148];
	DdrConfigData->DdrSpdInfo[Channel].ODT_DRAM_1866 = Buffer[149];
	DdrConfigData->DdrSpdInfo[Channel].ODT_DRAM_2400 = Buffer[150];
	DdrConfigData->DdrSpdInfo[Channel].ODT_DRAM_3200 = Buffer[151];
	DdrConfigData->DdrSpdInfo[Channel].PARK_DRAM_1866 = Buffer[152];
	DdrConfigData->DdrSpdInfo[Channel].PARK_DRAM_2400 = Buffer[153];
	DdrConfigData->DdrSpdInfo[Channel].PARK_DRAM_3200 = Buffer[154];

	DdrConfigData->DdrSpdInfo[Channel].rcd_num = (UINT16)Buffer[256];
	DdrConfigData->DdrSpdInfo[Channel].module_manufacturer_id = ((UINT16)Buffer[320] << 8) + Buffer[321];

	//tAAmin , Buffer[123] may be positive/negative
	DdrConfigData->DdrSpdInfo[Channel].tAAmin = Buffer[24] * SPD_MTB + (int8_t)Buffer[123] * SPD_FTB;
	//tRCDmin , Buffer[122] may be positive/negative
	DdrConfigData->DdrSpdInfo[Channel].tRCDmin = Buffer[25] * SPD_MTB + (int8_t)Buffer[122] * SPD_FTB;
	//tRPmin
	DdrConfigData->DdrSpdInfo[Channel].tRPmin = Buffer[26] * SPD_MTB + (int8_t)Buffer[121] * SPD_FTB;
	//tRASmin
	DdrConfigData->DdrSpdInfo[Channel].tRASmin = (((((UINT16)Buffer[27]) & 0xf)<<8) + Buffer[28]) * SPD_MTB;
	//tRCmin
	DdrConfigData->DdrSpdInfo[Channel].tRCmin = (((((UINT16)Buffer[27]>>4) & 0xf)<<8) + Buffer[29]) * SPD_MTB + (int8_t)Buffer[120] * SPD_FTB;
	//tFAWmin
	DdrConfigData->DdrSpdInfo[Channel].tFAWmin = ((((UINT16)Buffer[36] & 0xf) <<8) + Buffer[37]) * SPD_MTB;
	//tRDD_Smin
	DdrConfigData->DdrSpdInfo[Channel].tRRD_Smin = Buffer[38] * SPD_MTB + (int8_t)Buffer[119] * SPD_FTB;
	//tRDD_Lmin
	DdrConfigData->DdrSpdInfo[Channel].tRRD_Lmin = Buffer[39] * SPD_MTB + (int8_t)Buffer[118] * SPD_FTB;
	//tCCD_Lmin
	DdrConfigData->DdrSpdInfo[Channel].tCCD_Lmin = Buffer[40] * SPD_MTB + (int8_t)Buffer[117] * SPD_FTB;
	//tWRmin
	if((Buffer[42] == 0x0) && (Buffer[41] == 0x0) ){
	  DEBUG((EFI_D_INFO, "Error! spd byte42 = 0\n"));
	  DdrConfigData->DdrSpdInfo[Channel].tWRmin = 15000;
    } else {
	  DdrConfigData->DdrSpdInfo[Channel].tWRmin = Buffer[41] * SPD_MTB * 256 + Buffer[42] * SPD_MTB;
    }

	if((Buffer[43] == 0) && (Buffer[44] == 0) ){
		DEBUG((EFI_D_INFO, "Error! spd byte43 = 0\n"));
		DdrConfigData->DdrSpdInfo[Channel].tWTR_Smin = 2500;
    } else {
	  DdrConfigData->DdrSpdInfo[Channel].tWTR_Smin = ((((UINT16)Buffer[43] & 0xf) << 8) + Buffer[44]) * SPD_MTB;
    }

    if((Buffer[43] == 0) && (Buffer[45] == 0)) {
	  DdrConfigData->DdrSpdInfo[Channel].tWTR_Lmin = 7500;
    } else {
	  DdrConfigData->DdrSpdInfo[Channel].tWTR_Lmin = ((((UINT16)Buffer[43] >> 4) & 0xf) + Buffer[45]) * SPD_MTB;
    }

    PrintDdrInfo(Channel, DdrConfigData,MemoryDevInfo,SpdIndex);
    Temp = ((UINT16)Buffer[350] << 8) + Buffer[351];
    DEBUG((EFI_D_INFO, "/Dram:"));
    MemoryDevInfo->SpdInfo[SpdIndex].Manufacturer = Temp;
	switch(Temp){
		case SAMSUNG_VENDOR:
			DEBUG((EFI_D_INFO, "Samsung"));
            break;
		case MICRON_VENDOR:
			DEBUG((EFI_D_INFO, "Micron"));
            break;
		case HYNIX_VENDOR:
			DEBUG((EFI_D_INFO, "Hynix"));
            break;
		case KINGSTON_VENDOR:
			DEBUG((EFI_D_INFO, "KingSton"));
            break;
		case RAMAXEL_VENDOR:
			DEBUG((EFI_D_INFO, "Ramaxel"));
            break;
		case LANQI_VENDOR:
			DEBUG((EFI_D_INFO, "Lanqi"));
            break;
		case CXMT_VENDOR:
			DEBUG((EFI_D_INFO, "CXMT"));
            break;
		case UNILC_VENDOR:
			DEBUG((EFI_D_INFO, "Unilc"));
            break;
		default:
			DEBUG((EFI_D_INFO, "Unknown=0x%x", Temp));
	}
  Temp = ((UINT16)Buffer[320] << 8) + Buffer[321];
  MemoryDevInfo->SpdInfo[SpdIndex].ModuleManufacturerID = Temp;
  Temp = ((UINT16)Buffer[192] << 8) + Buffer[193];
  MemoryDevInfo->SpdInfo[SpdIndex].ModuleProductID = Temp;
  Temp = ((UINT16)Buffer[194] << 8) + Buffer[195];
  MemoryDevInfo->SpdInfo[SpdIndex].MemorSubsystemControllerManufacturerID = Temp;
  Temp = ((UINT16)Buffer[196] << 8) + Buffer[197];
  MemoryDevInfo->SpdInfo[SpdIndex].MemorSubsystemControllerProductID = Temp;

  Temp32 = ((UINT32)Buffer[325] << 24) + ((UINT32)Buffer[326] <<16)+ ((UINT32)Buffer[327] << 8) + Buffer[328];
  MemoryDevInfo->SpdInfo[SpdIndex].SerialNumber = Temp32;
  DEBUG((EFI_D_INFO, "/Serial:0x%x\n", Temp32));
  //while(1);
}

/**
  Probe memory module

  @param[in]      Channel memory module channel
  @param[in,out]  DDR configuration information

  @retval  EFI_SUCCESS  memory module exist.
           EFI_TIMEOUT  memory module don't exist.
**/
EFI_STATUS
DimmProbe (
  IN UINT8                      Channel,
  IN OUT DDR_CONFIG            *DdrConfigData,
  IN OUT MEMORY_DEVICES_INFO   *MemoryDevInfo,
  IN UINT8                      SpdIndex,
  IN UINT8                      Dimm
  )
{
  UINT64                        I2CBaseAddress;
  UINT32                        I2CSpeed;
  UINT32                        I2CSlaveAddress;
  UINT8                         Buffer[SPD_NUM *2] = {0};
  UINT8                         Temp;
  UINT8                         DramType;
  UINT8                        *SlaveAddress;

  SlaveAddress   = PcdGetPtr(PcdDdrI2cAddress);
  I2CBaseAddress = PcdGet64 (PcdSpdI2cControllerBaseAddress);
  I2CSpeed = PcdGet32 (PcdSpdI2cControllerSpeed);
  I2CSlaveAddress = SlaveAddress[SpdIndex];

  DEBUG((EFI_D_INFO, "I2CBase:%x, I2CSpeed:%x, I2CSlaveAddress:%x\n", I2CBaseAddress, I2CSpeed, I2CSlaveAddress));
  //
  // Initialize I2C Bus which contain SPD
  //

  i2c_init(I2CBaseAddress, I2CSpeed, I2CSlaveAddress);
  Temp = i2c_read(I2CBaseAddress, I2CSlaveAddress, 0, 1, Buffer, 256);
  if (Temp != 0) {
    DEBUG((EFI_D_ERROR, "Read I2C Failed\n"));
    return EFI_NOT_FOUND;
  }
  if (Dimm == 0){
    //Get Dram Type
    DramType = Buffer[2] & 0xf;
    DdrConfigData->DdrSpdInfo[Channel].dram_type = DramType;
    if (DramType == DDR3_TYPE) {
      ParseDDR3Spd(Channel, Buffer, DdrConfigData,MemoryDevInfo,SpdIndex);
      MemoryInfoDDR3(Channel,Buffer,MemoryDevInfo,SpdIndex);
    } else {
      spd_setpage(I2CBaseAddress, 0, 1);
      i2c_read(I2CBaseAddress, I2CSlaveAddress, 0, 1, &Buffer[256], 256);
      spd_setpage(I2CBaseAddress, 0, 0);
      ParseDDR4Spd(Channel, Buffer, DdrConfigData,MemoryDevInfo,SpdIndex);
      MemoryInfoDDR4(Channel,Buffer,MemoryDevInfo,SpdIndex);
    }
  }

  return EFI_SUCCESS;
}

/**
  Get DDR spd parameter

  @param[in]      Channel    Channel memory module channel
  @param[in,out]  DDR configuration information

  @retval  EFI_SUCCESS   get spd info success.
           EFI_NOT_FOUND memory module don't exist.
**/
EFI_STATUS
GetDdrSpdParameter(
  IN UINT8                     Channel,
  IN OUT DDR_CONFIG           *DdrConfigData,
  IN OUT MEMORY_DEVICES_INFO  *MemoryDevInfo,
  IN UINT8                     SpdIndex,
  IN UINT8                     Dimm
  )
{
  EFI_STATUS Status;

#ifdef MCU_USE_MANUAL
  DEBUG((EFI_D_INFO, "Manual Config DDR Parameter\n"));
  DdrConfigData->DdrSpdInfo[Channel].dimm_type = UDIMM_TYPE;
  DdrConfigData->DdrSpdInfo[Channel].data_width = DIMM_x8;
  DdrConfigData->DdrSpdInfo[Channel].mirror_type = NO_MIRROR;
  DdrConfigData->DdrSpdInfo[Channel].ecc_type = NO_ECC_TYPE;
  DdrConfigData->DdrSpdInfo[Channel].dram_type = DDR4_TYPE;
  DdrConfigData->DdrSpdInfo[Channel].rank_num = 1;
  DdrConfigData->DdrSpdInfo[Channel].row_num = 16;
  DdrConfigData->DdrSpdInfo[Channel].col_num = 10;
  DdrConfigData->DdrSpdInfo[Channel].bg_num = 4;
  DdrConfigData->DdrSpdInfo[Channel].bank_num = 4;
  DdrConfigData->DdrSpdInfo[Channel].tAAmin = 13125;
  DdrConfigData->DdrSpdInfo[Channel].tRCDmin = 13125;
  DdrConfigData->DdrSpdInfo[Channel].tRPmin = 13125;
  DdrConfigData->DdrSpdInfo[Channel].tRASmin = 24125;
  DdrConfigData->DdrSpdInfo[Channel].tRCmin = 45750;
  DdrConfigData->DdrSpdInfo[Channel].tFAWmin = 21000;
  DdrConfigData->DdrSpdInfo[Channel].tRRD_Smin = 4375;
  DdrConfigData->DdrSpdInfo[Channel].tRRD_Lmin = 5625;
  DdrConfigData->DdrSpdInfo[Channel].tCCD_Lmin = 5000;
  DdrConfigData->DdrSpdInfo[Channel].tWRmin = 15000;
  DdrConfigData->DdrSpdInfo[Channel].tWTR_Smin = 2500;
  DdrConfigData->DdrSpdInfo[Channel].tWTR_Lmin = 7500;
  PrintDdrInfo(Channel, DdrConfigData,,MemoryDevInfo,SpdIndex);
  DEBUG((EFI_D_INFO, "\n"));
#else
  if(DdrConfigData->ForceSpdEnable){
    DEBUG((EFI_D_INFO, "Read Parameter form Parameter Table\n"));
    PrintDdrInfo(Channel, DdrConfigData,MemoryDevInfo,SpdIndex);
    DEBUG((EFI_D_INFO, "\n"));
  } else {
    DEBUG((EFI_D_INFO, "Read Parameter form SPD\n"));
    Status = DimmProbe(Channel, DdrConfigData,MemoryDevInfo,SpdIndex,Dimm);
    if(EFI_ERROR(Status)) {
      return Status;
    }
  }
#endif
  return EFI_SUCCESS;
}

VOID DdrConfigInit(DDR_CONFIG *DdrConfigData)
{
  DdrConfigData->Head.Magic = PARAMETER_MCU_MAGIC;
  DdrConfigData->Head.Version = PARAM_MCU_VERSION;
  DdrConfigData->Head.Size = PARAM_MCU_SIZE;
  DdrConfigData->ChannelEnable = PARAM_CH_ENABLE;
  DdrConfigData->EccEnable = PARAM_ECC_ENABLE;
  DdrConfigData->DmEnable = PARAM_DM_ENABLE;
  DdrConfigData->ForceSpdEnable = PARAM_FORCE_SPD_DISABLE;
  DdrConfigData->MiscEnable = PARAM_MCU_MISC_ENABLE;
  DdrConfigData->TrainDebug = PARAM_TRAIN_DEBUF;
  DdrConfigData->TrainRecover = 0;
}

/**
  Get DDR config  parameter

  @param[in,out]  DDR    configuration information
  @param[in]      S3Flag S3 status

  @retval  None
**/
VOID
GetDdrConfigParameter (
  DDR_CONFIG            *DdrConfigData,
  UINT8                  S3Flag
  )
{
  UINT8                      Channel;
  UINT8                      Dimm;
  UINT8                      SpdIndex;
  UINT8                      ChannelCount;
  EFI_STATUS                 Status;
  UINT8                      DevicesNumber;
  UINTN                      DimmCount;
  UINT64                     Size;
  MEMORY_DEVICES_INFO       *MemoryDevInfo = NULL;
  UINT8                      Flag;
  UINT8                      Flag1;
  UINT8                      Flag2;

  DimmCount = PcdGetSize(PcdDdrI2cAddress);
  ChannelCount = PcdGet8(PcdDdrChannelCount);
  DevicesNumber = 2;
  SpdIndex = 0;
  Flag     = 0;
  Flag1    = 0;
  Flag2    = 0;

  DEBUG((EFI_D_INFO, "ChannelCount %d\n", ChannelCount));
  DEBUG((EFI_D_INFO, "DimmCount %d\n", DimmCount));
  MemoryDevInfo= AllocatePool(sizeof(MEMORY_DEVICES_INFO));
  SetMem(MemoryDevInfo,sizeof (MEMORY_DEVICES_INFO),0);

  if (DdrConfigData->ForceSpdEnable){
    ChannelCount = 2;
    MemoryDevInfo->DevicesNumber = 2;
    MemoryDevInfo->DimmCount = 2;
    for(Channel = 0; Channel < ChannelCount; Channel++) {
      if (!((DdrConfigData->ChannelEnable >> Channel) & 0x1)) {
        continue;
      }
      DEBUG((EFI_D_INFO, "Read Parameter form Parameter Table\n"));
      DEBUG((EFI_D_INFO, "\n"));
      MemoryDevInfo->SpdInfo[SpdIndex].ChannelNumer = Channel;
      MemoryDevInfo->SpdInfo[SpdIndex].DimmNumer = 0;

#ifdef NOTEBOOK_V2
      FillNoteBookMemoryInformation(MemoryDevInfo,SpdIndex);
#endif

      if (!(DdrConfigData->EccEnable >> Channel)) {
        DEBUG((EFI_D_INFO, "Ecc Disable\n"));
      }
      SpdIndex++;
    }
  } else {
    for(Channel = 0; Channel < ChannelCount; Channel++) {
      //DEBUG((DEBUG_ERROR, " %a %a line %d:\n\n\n",__FILE__,__func__,__LINE__));
      if (!((DdrConfigData->ChannelEnable >> Channel) & 0x1)) {
        continue;
      }
      for(Dimm = 0; Dimm < (DimmCount/2); Dimm ++){
        DEBUG((EFI_D_INFO, "Channel %d Dimm %d\n", Channel,Dimm));
        DEBUG((EFI_D_INFO, "SpdIndex %d\n", SpdIndex));
        Status = GetDdrSpdParameter(Channel, DdrConfigData,MemoryDevInfo,SpdIndex,Dimm);
        MemoryDevInfo->SpdInfo[SpdIndex].ChannelNumer = Channel;
        MemoryDevInfo->SpdInfo[SpdIndex].DimmNumer = Dimm;
        if (EFI_ERROR(Status)) {
          if (Dimm == 0) {
            DevicesNumber--;
          }
          DEBUG((EFI_D_INFO, "Channel %d Dimm %d Don't Probe\n\n", Channel,Dimm));
          Flag &= ~(0x1 << SpdIndex);
          if((SpdIndex == 0) || (SpdIndex == 2)){
            DdrConfigData->ChannelEnable &= ~(0x1 << Channel);
            DEBUG((EFI_D_INFO,"Channel %d Not Initialized!\n",Channel));
          }
        } else {
            Flag |= 0x1 << SpdIndex;
            if (!(DdrConfigData->EccEnable >> Channel)) {
              DEBUG((EFI_D_INFO, "Ecc Disable\n"));
            }
        }
      SpdIndex++;
      }
    }
  }
  DEBUG((EFI_D_INFO,"Flag is 0x%x!\n",Flag));
  Flag1 = Flag & (0x1<<1);
  Flag2 = Flag & (0x1<<3);
  Flag  = Flag1 | Flag2;
  if (Flag)
{
     DdrConfigData->MiscEnable |= 1<<4;
     DEBUG((EFI_D_INFO,"one driven two mode.\n"));
  }else{
    DdrConfigData->MiscEnable &= ~(0x1<<4);
    DEBUG((EFI_D_INFO,"one driven one mode.\n"));
  }

  if(DevicesNumber == 0){
    Alarm(ERROR_TYPE0);
    DEBUG((EFI_D_ERROR, " Don't Find Memory Devices-------\n"));
  }
  MemoryDevInfo->DevicesNumber = DevicesNumber;
  MemoryDevInfo->DimmCount = DimmCount;

  /* Creat DDR SpdInfo Hob */
  Size = sizeof(MEMORY_DEVICES_INFO);
  BuildGuidDataHob (&gPlatformMemoryInfoGuid, MemoryDevInfo, Size);
  FreePool(MemoryDevInfo);

  if(S3Flag) {
    DdrConfigData->TrainRecover = 0x1;
  }
  //while(1);
}

